using System;
using System.Collections;
using System.Collections.Generic;
using System.Html;
using System.Runtime.CompilerServices;
using Awaiter;
using jQueryApi;

namespace Nsb.Classes
{
    public delegate double EasingDelegate(int t, int millisecondsSince, int startValue, int endValue, int totalDuration);

    [IgnoreNamespace]
    public class Actor : Cluster
    {
        protected bool Acting;
        protected int FadeIn = 200;
        protected int FadeOut = 200;

        protected jQueryEventHandler FollowDelegate;
        protected int FollowTmr;
        protected int WaitTmr;
        public bool IsFollowable = true;
        protected int MouseX;
        protected int MouseY;
        protected bool TrackingDone;
        private List<Actor> _frames = new List<Actor>();

        public Actor FollowMouse()
        {
            UnfollowMouse();
            FollowDelegate =
                delegate(jQueryEvent e)
                {
                    MouseY = e.PageY;
                    MouseX = e.PageX;
                };
            jQuery.FromObject(Window.Self).Bind("mousemove.actor", FollowDelegate);
            Followable();
            StartActing();
            return this;
        }

        public Actor UnfollowMouse()
        {
            ResetTimers();
            if (FollowDelegate != null)
            {
                FollowDelegate = null;
                jQuery.FromObject(Window.Self).Unbind("mousemove.actor");
            }
            return this;
        }

        public virtual void RecordFrame()
        {
            Actor aa = (Actor)jQuery.Extend(new Actor(), this);
            aa.Element = Element.Clone(true, true);
            _frames.Add(aa);
        }

        public virtual Actor RewindTo(int idx)
        {
            Actor aa = _frames[idx];
            if (aa != null)
            {
                jQueryObject pr = Element;
                jQueryObject el = aa.Element.Clone(true, true);
                Element.ReplaceWith(el);
                Element = el;
                foreach (KeyValuePair<string, jQueryObject> p in Elements)
                {
                    if (p.Value == pr)
                        Elements[p.Key] = el;
                }
            }
            return this;
        }

        [AlternateSignature]
        public extern Actor Shake();

        public Actor Shake(int ms)
        {
            if (Acting)
                return this;
            Acting = true;
            ms = Script.Value(ms, 200);
            Script.Literal("jQuery.easing.shaker = {0}", (EasingDelegate)Shaker);
            jQueryPosition p = Element.Position();
            Element.CSS(p); // Fixes undefined top and left attributes
            Dictionary props = new Dictionary("top", p.Top + 5 + "px", "left", p.Left + 5 + "px");
            Dictionary opts = new Dictionary("duration", ms, "easing", "shaker", "complete", (Action)delegate { Element.CSS(p); });
            new Await()
                .AddAw(AnimateAw, props, opts)
                .AddDx(delegate { Acting = false; })
                .Commit();
            return this;
        }

        [AlternateSignature]
        public extern void AnimateAw(Await awp, object props);

        public void AnimateAw(Await awp, object props, object opts)
        {
            Dictionary op = Script.Value((Dictionary)opts, new Dictionary());
            Dictionary pp = Script.Value((Dictionary)props, new Dictionary());
            Action cp = Script.Value((Action)op["complete"], delegate { });
            op["complete"] =
                (Action)delegate
                         {
                             cp();
                             awp.Done();
                         };
            Element.Animate(pp, op);
        }

        [AlternateSignature]
        public extern void SlideInAw(Await awp);

        public void SlideInAw(Await awp, jQueryObject el)
        {
            el = el ?? Element;
            if (el != null)
            {
                int lf = Math.Max(el.Position().Left, Number.ParseInt(el.GetCSS("left")));
                el
                    .Show()
                    .CSS(new Dictionary("left", ((Math.Min(10, lf - 150))+"px"), "visibility", "visible"))
                    .Animate(new Dictionary("left", lf + "px"), 150, EffectEasing.Swing, delegate { awp.Done(); });
            }
        }

        private static double Shaker(int x, int t, int b, int c, int d)
        {
            return c * Math.Sin(t / d * 3 * 4 * (Math.PI / 2)) + b;
        }

        public virtual void ResetTimers()
        {
            Window.ClearTimeout(FollowTmr);
            Window.ClearTimeout(WaitTmr);
            FollowTmr = WaitTmr = 0;
        }

        private void Followable()
        {
            IsFollowable = true;
        }

        protected void StartActing()
        {
            Acting = true;
            TrackingDone = false;
            Window.ClearTimeout(FollowTmr);
            FollowTmr = Window.SetTimeout(Follow, 1);
            Element.FadeIn(FadeIn);
        }

        protected virtual void Follow()
        {
            //Element.Animate(new Dictionary("top", MouseY + "px", "left", MouseX + "px"), 100, EffectEasing.Linear);
            Element.CSS("top", MouseY + "px").CSS("left", MouseX + "px");
            Window.ClearTimeout(FollowTmr);
            FollowTmr = Window.SetTimeout(Follow, 30);
        }

        protected void WaitTrackingDoneAw(Await awp)
        {
            if (!TrackingDone)
            {
                Window.ClearTimeout(WaitTmr);
                WaitTmr = Window.SetTimeout(delegate { WaitTrackingDoneAw(awp); }, 13);
                return;
            }
            awp.Done();
        }

        public Actor Viewit(string url)
        {
            return (Actor)new Actor()
                .FromHtml("<div/>")
                .Html("<iframe src='" + url + "&output=embed" + "' frameborder='0' width='480' height='389' allowfullscreen='false' webkitallowfullscreen='false'></iframe>")
                .AppendTo(Element)
                ;
        }

        // ReSharper disable CSharpWarnings::CS0108
        public Actor Html(string html)
        // ReSharper restore CSharpWarnings::CS0108
        {
            return (Actor)base.Html(html);
        }

        // ReSharper disable CSharpWarnings::CS0108
        public Actor Append(jQueryObject el)
        // ReSharper restore CSharpWarnings::CS0108
        {
            return (Actor)base.Append(el);
        }

        // ReSharper disable CSharpWarnings::CS0108
        public Actor Prepend(jQueryObject el)
        // ReSharper restore CSharpWarnings::CS0108
        {
            return (Actor)base.Prepend(el);
        }

        [AlternateSignature]
        // ReSharper disable CSharpWarnings::CS0108
        public extern Actor AppendTo(string sel);
        // ReSharper restore CSharpWarnings::CS0108

        // ReSharper disable CSharpWarnings::CS0108
        public Actor AppendTo(jQueryObject el)
        // ReSharper restore CSharpWarnings::CS0108
        {
            return (Actor)base.AppendTo(el);
        }

        // ReSharper disable CSharpWarnings::CS0108
        public Actor PrependTo(jQueryObject el)
        // ReSharper restore CSharpWarnings::CS0108
        {
            return (Actor)base.PrependTo(el);
        }
        // ReSharper disable CSharpWarnings::CS0108
        public Actor AddClass(string classNames)
        // ReSharper restore CSharpWarnings::CS0108
        {
            return (Actor)base.AddClass(classNames);
        }
    }
}